
#include "regexp.h"
#include "automata.h"
#include <string.h>
#include <ctype.h>

#define ERROR(p)    do {rexp.errPos = p;
#define ERREND      MarkError();system("pause");exit(-1);} while(0)

#define DEBUGM      //printf

#define LOG

// A-Z, a-z, 0-9, lambda (^)
#define MAX_ALPHABET_SIZE   63

int alphabet_size;

RegExpInfo rexp;

/*
int maind(void)
{
    t_table *t;
    int i;

    SetLog(NULL);

    SetRegExp("(abcd|f)+|ccd*");
    t = TranslateRegExp();

    
    addInputChain(t, "cc");
    addInputChain(t, "abcd");
    addInputChain(t, "abcdffabcdff");
    addInputChain(t, "ccdddddd");

    addInputChain(t, "abcdfabcdfccd");
    addInputChain(t, "cabada");
    addInputChain(t, "cccd");
    addInputChain(t, "abfacfabcd");
    

    printf("%d Inputs added.\n", t->input.dim);
    for (i = 0; i < t->input.dim; i++)
        printf("\tChain %d: %s (%p)\n", i, t->input.chains[i], t->input.chains[i]);

    system("pause");

    

    printf("\n\nExpression: \"%s\"\nPos: %d  Length: %d\nStates: %d   "
            "lastState: %d   iniState: %d   finState: %d\n",
            rexp.exp, rexp.pos, rexp.n, rexp.numStates, rexp.lastState,
            rexp.iniState, rexp.finState);



    SetLog("table.txt");
    LogTable(t);
    SetLog(NULL);

    
    removeLambdaTrans(t);
    printf("\n\nLAMBDA DONE\n\n");
    printTable(t);
    system("pause");
    nfaToDfa(t);
    printf("\n\nDFA DONE\n\n");
    printTable(t);
    printf("IniState: %d\n", t->iniState);
    printf("Estados finales:\n");
    //printStates2(&t->finalStates);
    
    for (i = 0; i < t->states.dim; i++)
        if (isFinalStInd(t, i))
            printf("%d ", i);
    printf("\n");
    
    system("pause");
    MinimizeDFA(t);
    printf("\n\nMIN DONE\n\n");
    printTable(t);
    system("pause");
    testInput(t);
    
    printf("%d Inputs tested.\n", t->input.dim);
    for (i = 0; i < t->input.dim; i++)
        printf("\tTest %d: %s\n", i, t->input.chains[i]);

    system("pause");
    
}
*/

void SetRegExp(char *exp)
{
    rexp.n = strlen(exp);
    rexp.exp = exp;
    rexp.pos = 0;
    
    initAutomaton();

    return;
}

t_table *TranslateRegExp(void)
{
    RexBlock b;
        
    b = getTerms();
    
    rexp.iniState = b.in;
    rexp.finState = b.out;
    
    if (rexp.pos < rexp.n)
        ERROR(rexp.pos+1)
            LogMsg("Error: ending expected at position %d\n", rexp.pos+1);
        ERREND;

    fixAutomaton();
    
    return rexp.t;
}

void MarkError(void)
{
    int i;
    LogMsg("\n\t%s\n\t", rexp.exp);
    for (i = 1; i < rexp.errPos; i++)
        LogMsg(" ");
    
    LogMsg("^\n");
    
}


void initAutomaton(void)
{
    t_table *t;
    int i;
    char c;
    int chars[256] = {0};
    int usedChars = 0;
    
    rexp.numStates = 0;
    rexp.lastState = 0;
    
    // Calculate the number of states needed, 
    // and check all characters are valid
    for (i = 0; i < rexp.n; i++)
    {
        if (isalnum(c = rexp.exp[i]))
        {
            rexp.numStates += 2;
            chars[c] = 1;
        }
        else if (strchr("|*+", c) != NULL)
            rexp.numStates += 2;
        else if (c == '^')
            rexp.numStates++;
        else if (strchr("()", c) == NULL)
            ERROR(i+1)
                LogMsg("Error: invalid character in input expression\n");
            ERREND;
    }

//    printf("Numstates: %d\n", rexp.numStates);
    
    if ((t = malloc(sizeof(t_table))) == NULL)
        ERROR(0)
            LogMsg("Error: malloc failed\n");
        ERREND;
    
    // ALPHABET_SIZE es la cantidad de simbolos + 1 (63)
    if ((t->alphabet = malloc(MAX_ALPHABET_SIZE)) == NULL)
        ERROR(0)
            LogMsg("Error: malloc failed\n");
        ERREND;

    for (i = 0; i < 256; i++)
        if (chars[i])
            t->alphabet[usedChars++] = i;

    t->alphabet[usedChars] = 0;
    
    alphabet_size = usedChars+1;
    
/*    
    strcpy(t->alphabet, "abcdefghijklmnopqrstuvwxyz"
                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                        "0123456789");
*/    
    
    createVec(&t->states, rexp.numStates);
    createVec(&t->finalStates, 1);
    

    t->transitions = buildMatrix(rexp.numStates, alphabet_size);

    t->originalDim = rexp.numStates;
    t->originalFinalDim = 1;
    t->states.dim = rexp.numStates;
    t->finalStates.dim = 1;
    rexp.t = t;

    for (i = 0; i < t->states.dim; i++)
        turnOn(t->states.vec[i], i);
    
    t->input.chains = NULL;
    t->input.dim = 0;


    return;
}


void fixAutomaton(void)
{
    rexp.t->iniState = rexp.iniState;
    longIntCpy(rexp.t->finalStates.vec[0], rexp.t->states.vec[rexp.finState]);
}



RexBlock addLambdaBlock(void)
{
    RexBlock b;
    
    b.in = rexp.lastState++;
    b.out = b.in;

    DEBUGM(" Lambda block added. in: %d, out: %d.\n", b.in, b.out);
    
    return b;
}

RexBlock addSymbolBlock(char symbol)
{
    RexBlock b;
    
    
    b.in = rexp.lastState++;
    b.out = rexp.lastState++;
    
    addTransition(rexp.t, b.in, getSymIndex(rexp.t->alphabet, symbol), b.out);  

    DEBUGM(" Symbol (%c) block added. in: %d, out: %d.\n", symbol, b.in, b.out);
    
    return b;
}

RexBlock addSplitBlock(RexBlock b1, RexBlock b2)
{
    RexBlock b;
    
    b.in = rexp.lastState++;
    b.out = rexp.lastState++;
    
    addTransition(rexp.t, b.in, alphabet_size-1, b1.in);
    addTransition(rexp.t, b.in, alphabet_size-1, b2.in); 
    addTransition(rexp.t, b1.out, alphabet_size-1, b.out);
    addTransition(rexp.t, b2.out, alphabet_size-1, b.out); 

    DEBUGM("Split block added. in: %d, out: %d. Blocks %d-%d, %d-%d\n",
            b.in, b.out, b1.in, b1.out, b2.in, b2.out);


    return b;
}

RexBlock addWrapperBlock(RexBlock b1)
{
    RexBlock b;
    
    b.in = rexp.lastState++;
    b.out = rexp.lastState++;
    
    addTransition(rexp.t, b.in, alphabet_size-1, b1.in);
    addTransition(rexp.t, b1.out, alphabet_size-1, b.out); 

//    DEBUGM("Wrapper block added. in: %d, out: %d. Blocks %d-%d, %d-%d\n",
//            b.in, b.out, b1.in, b1.out, b2.in, b2.out);

//    printf("Agregamos wrapper\n");

    return b;
}


RexBlock addConcatBlock(RexBlock b1, RexBlock b2)
{
    RexBlock b;
    
    b.in = b1.in;
    b.out = b2.out;
    
    addTransition(rexp.t, b1.out, alphabet_size-1, b2.in);

    DEBUGM(" Concat block added. in: %d, out: %d. Blocks %d-%d, %d-%d\n",
            b.in, b.out, b1.in, b1.out, b2.in, b2.out);

    return b;
}


int addBlockModifier(RexBlock b, ModType m)
{
    switch(m)
    {
        case ASTERISK:
            addTransition(rexp.t, b.in, alphabet_size-1, b.out);
        case PLUS:
            addTransition(rexp.t, b.out, alphabet_size-1, b.in);
    }

    DEBUGM("  Block (%d-%d) modifier added: %s.\n", b.in, b.out, modNames[m]);
    
    return (m != NONE);
}


// Processes "terms" (OR-wise)
RexBlock getTerms(void)
{
    char c;
    RexBlock b;
    
    c = rexp.exp[rexp.pos];
    
    if (c == '(' || c == '^' || isalnum(c))
    {
        b = getFactors();
    }
    else if (c == 0)
    {
        ERROR(rexp.pos+1)
            LogMsg("Error: unexpected ending at position %d\n", rexp.pos + 1);
        ERREND;
    }
    else
    {
        ERROR(rexp.pos+1)
            LogMsg("Error: unexpected char '%c' at position %d\n", 
            c, rexp.pos + 1);
        ERREND;
    }

    c = rexp.exp[rexp.pos];

    if (c == '|')
    {   
        rexp.pos++;
        return addSplitBlock(b, getTerms());
    }
    else if (c == 0 || c == ')')
    {
        return b;
    }
    else
    {
        ERROR(rexp.pos+1)
            LogMsg("Error: unexpected char '%c' at position %d\n",
                c, rexp.pos + 1);
        ERREND;
    }

}


// Processes "factors" (concatenation-wise)
RexBlock getFactors(void)
{
    char c;
    RexBlock b;
    
    c = rexp.exp[rexp.pos];

    if (c == '(')
    {
        rexp.pos++;
        b = getTerms();

        if (rexp.exp[rexp.pos] != ')')
            ERROR(rexp.pos+1)
                LogMsg("Error: closing bracket missing at char %d\n",
                rexp.pos + 1);
            ERREND;
        
        rexp.pos++;
    }
    else if (isalnum(c))
    {
        b = addSymbolBlock(c);
        rexp.pos++;
    }
    else if (c == '^')
    {
        b = addLambdaBlock();
        rexp.pos++;
    }
    else if (c == 0)
    {
        ERROR(rexp.pos+1)
            LogMsg("Error: unexpected ending at position %d\n", rexp.pos + 1);
        ERREND;
    }
    else
    {
        rexp.pos++;
        ERROR(rexp.pos)
            LogMsg("Error: unexpected char '%c' at position %d\n", c, rexp.pos);
        ERREND;
    }
    
    if (addBlockModifier(b, getMods()))
        b = addWrapperBlock(b);

    c = rexp.exp[rexp.pos];

    if (isalnum(c) || c == '^' || c == '(')
    {   
        //rexp.pos++;
        return addConcatBlock(b, getFactors());
    }
    else if (c == ')' || c == '|' || c == 0)
    {   
        return b;
    }
    else
    {
        ERROR(rexp.pos)
            LogMsg("Loquisimo!!\n");
        ERREND;
    }

}

ModType getMods(void)
{
    char c;
    ModType m = NONE;
    int i = 0;
    
    
    
    while((c = rexp.exp[rexp.pos]) == '*' || c == '+')
    {
        if (i > 0)
            ERROR(rexp.pos+1)
                LogMsg("\nError: unexpected character '%c' at position %d. "
                       "Consecutive '*' or '+' characters are not allowed.",
                       c, rexp.pos+1);
            ERREND;
        
        rexp.pos++;
        
        if (c == '+' && m == NONE)
            m = PLUS;
        else if (c == '*')
            m = ASTERISK;
        
        i++;
    }
    
    return m;
}



void LogTable(t_table * t, char *name)
{
    int i,j;

    LogMsg("\n------------------------------------------------------------"
           "\nPrinting transition table %s...\n", name);
    LogMsg("   |");
    for(i=0;i<t->transitions.cols-1;i++)
        LogMsg("   %c",t->alphabet[i]);
    LogMsg("    ^\n");
    for(i=0;i<t->transitions.cols*4+4;i++)
        LogMsg("-");
    LogMsg("\n");
        
    for(i=0;i<t->transitions.fils;i++)
    {
        LogMsg("%3d|",i);
        for(j=0;j<t->transitions.cols-1;j++)
        {
            if (t->transitions.data[i][j] == -1)
                LogMsg("  ..");
            else
                LogMsg("%4d",t->transitions.data[i][j]);
        }   
        
        LogMsg("  ");
        
        if (t->transitions.data[i][j] != -1)
            printState(t->states.vec[t->transitions.data[i][j]]);
        else
            LogMsg(" ... ");
        
            
            
        LogMsg("\t%3d  %s\n", i, isFinalStInd(t, i)?"f":"");
    }
    
    LogMsg("\nEstado inicial: %d\n", t->iniState);

    LogMsg("\nEstados:\n\n");
    printStates2(&t->states);
    
}



/*    
int getNewState(void)
{
    int i;
    
    //Allocate more space if needed
    if(t->states.dim == t->states.maxDim)
    {
        if((aux = realloc(t->states.vec,
            sizeof(t_longInt)* (t->states.maxDim *= 2)))==NULL)
                error(1,"Could not allocate memory. Shame on you.");
        else
            t->states.vec = aux;
    }

    reset(t->states.vec[t->states.dim]);
    turnOn(t->states.vec[t->states.dim], t->states.dim);

    t->states.dim++;

    t->transitions.fils++;
    
    // Realloc matrix if needed (every 20 states)
    if (t->transitions.fils % 20 == 0)
        realloc(t->transitions.data, (t->transitions.fils + 20) * sizeof(int*));

    // Clear transitions for this new state
    for (i = 0; i < ALPHABET_SIZE; i++)
        t->transitions[t->transitions.fils-1][i] = -1;
//    return lastState++;
}
*/

